/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package me.majiajie.pagerbottomtabstrip.widget;

import me.majiajie.pagerbottomtabstrip.util.CustomAnimatorStateChangedListener;
import me.majiajie.pagerbottomtabstrip.util.EventUtil;
import me.majiajie.pagerbottomtabstrip.util.MyValueAnimator;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 水波纹布局
 */
public class RippleLayout extends DependentLayout implements Component.EstimateSizeListener, Component.DrawTask, Component.TouchEventListener {

    private final String IS_RIPPLE = "isRipple"; // 是否具有水波纹效果

    private int color = 0x22999999;
    private long time = 200;
    private List<Oval> mOvals;
    private Paint mPaint;
    private int width;
    private int height;
    private boolean isRipple = true;

    public RippleLayout(Context context) {
        this(context, null);
    }

    public RippleLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public RippleLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(attrSet);
    }

    private void init(AttrSet attrSet) {
        if (attrSet != null) {
            if (attrSet.getAttr(IS_RIPPLE).isPresent()) {
                isRipple = attrSet.getAttr(IS_RIPPLE).get().getBoolValue();
            }
        }
        mOvals = new ArrayList<>();
        mPaint = new Paint();
        mPaint.setColor(new Color(color));
        setEstimateSizeListener(this);
        addDrawTask(this, BETWEEN_BACKGROUND_AND_CONTENT);
        setTouchEventListener(this);
    }

    /**
     * 设置是否具有水波纹背景效果
     *
     * @param isRipple 是否具有水波纹背景效果
     */
    public void isRipple(boolean isRipple) {
        this.isRipple = isRipple;
    }

    /**
     * 设置水波纹的颜色
     *
     * @param color argb
     */
    public void setColor(int color) {
        this.color = color;
        mPaint.setColor(new Color(color));
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        width = EstimateSpec.getSize(widthMeasureSpec);
        height = EstimateSpec.getSize(heightMeasureSpec);
        return false;
    }

    /**
     * 尺寸发生变化时调用监听，处理水波纹
     *
     * @param width  控件的宽，即水波纹的最大宽
     * @param height 控件的高，即水波纹的最大高
     */
    public void onSizeChanged(int width, int height) {
        this.width = width;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (isRipple && touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            float touchX = EventUtil.getXInComponent(component, touchEvent);
            float touchY = EventUtil.getYInComponent(component, touchEvent);
            addOval(touchX, touchY);
            return true;
        }
        return false;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        Iterator<Oval> iterator = mOvals.iterator();
        while (iterator.hasNext()) {
            Oval oval = iterator.next();
            if (oval.radius < getMaxRadius(oval.centerX, oval.centerY) && (oval.radius != oval.lastRadius)) {
                canvas.drawOval(new RectFloat(oval.getLeft(), oval.getTop(), oval.getRight(), oval.getBottom()), mPaint);
                oval.lastRadius = oval.radius;
            } else {
                canvas.drawRect(0, 0, width, height, mPaint);
                iterator.remove();
            }
            getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
                @Override
                public void run() {
                    invalidate();
                }
            });
        }
    }

    private void addOval(float centerX, float centerY) {
        final Oval oval = new Oval(Math.min(width, height) / 2, centerX, centerY);
        mOvals.add(oval);

        MyValueAnimator valueAnimator = MyValueAnimator.ofFloat(0, 1);
        valueAnimator.setCurveType(Animator.CurveType.LINEAR);
        valueAnimator.setDuration(time);
        valueAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                oval.radius += (getMaxRadius(centerX, centerY) - oval.radius) * value;
            }
        });
        valueAnimator.setStateChangedListener(new CustomAnimatorStateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                invalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     * 以矩形内一点为圆心画圆，覆盖矩形，求这个圆的最小半径
     *
     * @param centerX 横坐标
     * @param centerY 纵坐标
     * @return 最小半径
     */
    private float getMaxRadius(float centerX, float centerY) {
        double r1Square = centerX * centerX + centerY * centerY;
        double r2Square = (width - centerX) * (width - centerX) + centerY * centerY;
        double r3Square = (width - centerX) * (width - centerX) + (height - centerY) * (height - centerY);
        double r4Square = centerX * centerX + (height - centerY) * (height - centerY);

        return (float) Math.sqrt(Math.max(Math.max(r1Square, r2Square), Math.max(r3Square, r4Square)));
    }

    private class Oval {
        float radius;
        float centerX;
        float centerY;
        float lastRadius;

        Oval(float radius, float centerX, float centerY) {
            this.radius = radius;
            this.centerX = centerX;
            this.centerY = centerY;
            this.lastRadius = -1;
        }

        float getLeft() {
            return centerX - radius;
        }

        float getTop() {
            return centerY - radius;
        }

        float getRight() {
            return centerX + radius;
        }

        float getBottom() {
            return centerY + radius;
        }
    }

}
